﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Reflection;
using System.Linq;
using System.Collections.Generic;

namespace MoneyBook.Reflection {
    public class EnhancePropertyAttributeType : Type {

        private Type sourceType;
        private Dictionary<PropertyInfo, PropertyInfo> customProperties = new Dictionary<PropertyInfo,PropertyInfo>();

        public EnhancePropertyAttributeType(Type sourceType) {
            this.sourceType = sourceType;
        }

        public override Assembly Assembly {
            get { return this.sourceType.Assembly; }
        }

        public override string AssemblyQualifiedName {
            get { return this.sourceType.AssemblyQualifiedName; }
        }

        public override Type BaseType {
            get { return this.sourceType.BaseType; }
        }

        public override string FullName {
            get { return this.sourceType.FullName; }
        }

        public override Guid GUID {
            get { return this.sourceType.GUID; }
        }

        protected override TypeAttributes GetAttributeFlagsImpl() {
            return this.sourceType.Attributes;
        }

        protected override ConstructorInfo GetConstructorImpl(BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) {
            return this.sourceType.GetConstructor(bindingAttr, binder, types, modifiers);
        }

        public override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr) {
            return this.sourceType.GetConstructors(bindingAttr);
        }

        public override Type GetElementType() {
            return this.sourceType.GetElementType();
        }

        public override EventInfo GetEvent(string name, BindingFlags bindingAttr) {
            return this.sourceType.GetEvent(name, bindingAttr);
        }

        public override EventInfo[] GetEvents(BindingFlags bindingAttr) {
            return this.sourceType.GetEvents(bindingAttr);
        }

        public override FieldInfo GetField(string name, BindingFlags bindingAttr) {
            return this.sourceType.GetField(name, bindingAttr);
        }

        public override FieldInfo[] GetFields(BindingFlags bindingAttr) {
            return this.sourceType.GetFields(bindingAttr);
        }

        public override Type GetInterface(string name, bool ignoreCase) {
            return this.sourceType.GetInterface(name, ignoreCase);
        }

        public override Type[] GetInterfaces() {
            return this.sourceType.GetInterfaces();
        }

        //TODO?
        public override MemberInfo[] GetMembers(BindingFlags bindingAttr) {
            return this.sourceType.GetMembers(bindingAttr);
        }

        protected override MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) {
            return this.sourceType.GetMethod(name, bindingAttr, binder, callConvention, types, modifiers);
        }

        public override MethodInfo[] GetMethods(BindingFlags bindingAttr) {
            return this.sourceType.GetMethods(bindingAttr);
        }

        public override Type GetNestedType(string name, BindingFlags bindingAttr) {
            return this.sourceType.GetNestedType(name, bindingAttr);
        }

        public override Type[] GetNestedTypes(BindingFlags bindingAttr) {
            return this.sourceType.GetNestedTypes(bindingAttr);
        }

        //TODO
        public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) {
            return this.sourceType.GetProperties(bindingAttr)
                .Select(info => GetCustomPropertyInfo(info))
                .ToArray();
        }

        //TODO
        protected override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers) {
            PropertyInfo info = null;
            if (name != null && bindingAttr != BindingFlags.Default && returnType != null && types != null && modifiers != null) {
                info = this.sourceType.GetProperty(name, bindingAttr, binder, returnType, types, modifiers);
            } else if (name != null && returnType != null && types != null && modifiers != null) {
                info = this.sourceType.GetProperty(name, returnType, types, modifiers);
            } else if (name != null && returnType != null && types != null) {
                info = this.sourceType.GetProperty(name, returnType, types);
            } else if (name != null && returnType != null) {
                info = this.sourceType.GetProperty(name, returnType);
            } else if (name != null && bindingAttr != BindingFlags.Default) {
                info = this.sourceType.GetProperty(name, bindingAttr);
            } else if (name != null) {
                info = this.sourceType.GetProperty(name);
            }
            if (info != null) {
                return GetCustomPropertyInfo(info);
            }
            return null;
        }

        private PropertyInfo GetCustomPropertyInfo(PropertyInfo prop) {
            if (!this.customProperties.ContainsKey(prop)) {
                this.customProperties.Add(prop, new EnhancePropertyAttributePropertyInfo(prop, sourceType));
            }
            return this.customProperties[prop];

        }

        protected override bool HasElementTypeImpl() {
            return this.sourceType.HasElementType;
        }

        public override object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, System.Globalization.CultureInfo culture, string[] namedParameters) {
            return this.sourceType.InvokeMember(name, invokeAttr, binder, target, args);
        }

        protected override bool IsArrayImpl() {
            return this.sourceType.IsArray;
        }

        protected override bool IsByRefImpl() {
            return this.sourceType.IsByRef;
        }

        protected override bool IsCOMObjectImpl() {
            return this.sourceType.IsCOMObject;
        }

        protected override bool IsPointerImpl() {
            return this.sourceType.IsPointer;
        }

        protected override bool IsPrimitiveImpl() {
            return this.sourceType.IsPrimitive;
        }

        public override Module Module {
            get { return this.sourceType.Module; }
        }

        public override string Namespace {
            get { return this.sourceType.Namespace; }
        }

        public override Type UnderlyingSystemType {
            get { return this.sourceType.UnderlyingSystemType; }
        }

        public override object[] GetCustomAttributes(Type attributeType, bool inherit) {
            return this.sourceType.GetCustomAttributes(attributeType, inherit);
        }

        public override object[] GetCustomAttributes(bool inherit) {
            return this.sourceType.GetCustomAttributes(inherit);
        }

        public override bool IsDefined(Type attributeType, bool inherit) {
            return this.sourceType.IsDefined(attributeType, inherit);
        }

        public override string Name {
            get { return this.sourceType.Name; }
        }
    }
}
